/*
 * BoundingSphere.h
 *
 * Created 7/20/2009 By Johnny Huynh
 *
 * Version 00.00.01 7/20/2009
 *
 * Copyright Information:
 * All content copyright © 2009 Johnny Huynh. All rights reserved.
 */

 #ifndef BoundingSphere_H
 #define BoundingSphere_H
 
 // Bounding Sphere
 template <typename TYPENAME> class BoundingSphere;
 
 typedef BoundingSphere<GLfloat> BSf;
 typedef BoundingSphere<GLdouble> BSd;
 
 #include "Vector3.h"
 #include "GL_Render_Shape.h"

 template <typename TYPENAME>
 class BoundingSphere
 {
 // Data Members
 protected:
    TYPENAME radius;
	const Vector3<TYPENAME> raw_center; // (x, y, z) - center point of this OBB relative to position of reference 
	                                    // at position (0, 0, 0)
	Vector3<TYPENAME> center; // (x, y, z) - center point of this OBB
	const Vector3<TYPENAME>* position_reference_Ptr; // pointer to the position of reference 
	                                             // (for example: position of the object encapsulated by this Bounding Sphere
 
 // Local Functions
 public:
    BoundingSphere( const Vector3<TYPENAME> * position_reference_Ptr,
                    const TYPENAME& radius, 
                    const TYPENAME& raw_centerx, const TYPENAME& raw_centery, const TYPENAME& raw_centerz );
    BoundingSphere( const BoundingSphere<TYPENAME>& bs );
    BoundingSphere( const BoundingSphere<TYPENAME>& bs, const Vector3<TYPENAME> * position_reference_Ptr ); 
    ~BoundingSphere();
    inline BoundingSphere<TYPENAME>& operator=( const BoundingSphere<TYPENAME>& bs );
    
 // Friend Functions
    template< typename TYPENAME > friend inline bool collide( const BoundingSphere<TYPENAME>& bsA, 
                                                              const BoundingSphere<TYPENAME>& bsB );
    template <typename TYPENAME> friend inline BoundingSphere<TYPENAME>& maintain( const BoundingSphere<TYPENAME>& bs );
    template <typename TYPENAME> friend inline void print( const BoundingSphere<TYPENAME>& bs );
    template <typename TYPENAME> friend inline void render( const BoundingSphere<TYPENAME>& bs );
 };

 /** LOCAL FUNCTIONS **/
 
 /**
  * Constructor
  */
 template< typename TYPENAME >
 BoundingSphere<TYPENAME>::BoundingSphere( const Vector3<TYPENAME> * position_reference_Ptr,
                                           const TYPENAME& radius,
                                           const TYPENAME& raw_centerx, 
                                           const TYPENAME& raw_centery,
                                           const TYPENAME& raw_centerz )
                            : position_reference_Ptr( position_reference_Ptr ),
                              radius( radius ),
                              raw_center( raw_centerx, raw_centery, raw_centerz ),
                              center( raw_center + *position_reference_Ptr ) // maintain the bounding sphere
 {
    //maintain( *this );
 }
 
 /**
  * Copy Constructor
  */
 template <typename TYPENAME>
 BoundingSphere<TYPENAME>::BoundingSphere( const BoundingSphere<TYPENAME>& bs ) 
               : position_reference_Ptr( bs.position_reference_Ptr ),
                 radius( bs.radius ),
                 raw_center( bs.raw_center ),
                 center( bs.center )
 {
    
 }
 
 /**
  * Alternative Constructor
  */
 template <typename TYPENAME>
 BoundingSphere<TYPENAME>::BoundingSphere( const BoundingSphere<TYPENAME>& bs, 
                                           const Vector3<TYPENAME> * position_reference_Ptr ) 
               : position_reference_Ptr( position_reference_Ptr ),
                 radius( bs.radius ),
                 raw_center( bs.raw_center ),
                 center( bs.center )
 {
    
 }
 
 /**
  * Destructor
  */
 template< typename TYPENAME >
 BoundingSphere<TYPENAME>::~BoundingSphere()
 {

 }
 
 /**
  * operator=() copies the content of the specified bounding sphere to this bounding sphere.
  *
  * @param (const BoundingSphere<TYPENAME>&) bs
  */
 template< typename TYPENAME >
 inline BoundingSphere<TYPENAME>& BoundingSphere<TYPENAME>::operator=( const BoundingSphere<TYPENAME>& bs )
 {
    memcpy( this, &bs, sizeof( BoundingSphere<TYPENAME> ) );
    
    return *this;
 }
 
 /** FRIEND FUNCTIONS **/
 
 /**
  * collide() returns true if the two specified bounding spheres intersect; otherwise,
  * false is returned.
  *
  * @param (const BoundingSphere<TYPENAME>&) bsA
  * @param (const BoundingSphere<TYPENAME>&) bsB
  * @return bool
  */
 template< typename TYPENAME >
 inline bool collide( const BoundingSphere<TYPENAME>& bsA, const BoundingSphere<TYPENAME>& bsB )
 {
    return magnitude_squared( bsB.center - bsA.center ) <= (bsA.radius * bsA.radius) + (bsB.radius * bsB.radius);
 }
 
 /**
  * maintain() recomputes the center using the raw_center and
  * the position of reference.
  *
  * @param (const BoundingSphere<TYPENAME>&) bs
  * @return BoundingSphere<TYPENAME>&
  */
 template <typename TYPENAME>
 inline BoundingSphere<TYPENAME>& maintain( const BoundingSphere<TYPENAME>& bs )
 {
    bs.center = bs.raw_center + *bs.position_reference_Ptr;
 
    return bs;
 }
 
 /**
  * print() prints out information about this BoundingSphere.
  *
  * @param (const BoundingSphere<TYPENAME>&) bs
  */
 template <typename TYPENAME>
 inline void print( const BoundingSphere<TYPENAME>& bs )
 {
    printf("BoundingSphere\n");
    printf("    radius: %1f\n", bs.radius);
    printf("    center: ");
    print( bs.center );
 }
 
 /**
  * render() contains the code necessary to render the bounding sphere.
  *
  * @param (const BoundingSphere<TYPENAME>&) bs
  */
 template <typename TYPENAME> 
 inline void render( const BoundingSphere<TYPENAME>& bs )
 {
    Render_Shape::setColor( ZERO, ZERO, ONE );
    Render_Shape::Sphere_WireFrame( bs.center, bs.radius );
 }
 
 #endif // BoundingSphere